/*
 * Author: hwch25
 * Description: 显示CPU，WIFI，SSD，NV显卡温度和CPU使用率和频率.
 * Version: 18
 * GNOME Shell Tested: 47
 * GNOME Shell Supported: 45, 46, 47
 * GitHub: https://gitee.com/hwch25/temp
 * 
 * Credits: Michael Knap - System Monitor Tray Indicator - https://github.com/michaelknap/gnome-system-monitor-indicator
 * License: MIT License
 */

'use strict';

import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import St from 'gi://St';
import Clutter from 'gi://Clutter';
import GObject from 'gi://GObject';

import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';

import { Button } from 'resource:///org/gnome/shell/ui/panelMenu.js';
import { PopupMenuItem, PopupSubMenuMenuItem } from 'resource:///org/gnome/shell/ui/popupMenu.js';
import { panel } from 'resource:///org/gnome/shell/ui/main.js';

Gio._promisify(Gio.Subprocess.prototype, 'communicate_utf8_async');


// 定义系统监控器指示器的主类
export class HwchTemp extends Button {

  // 初始化指示器
  _init(settings) {
    super._init(0, "资源显示器", false);
    this._settings = settings;

    // 创建一个布局框，初始化 Label 并添加到 box，将框添加到 'this' actor
    this.box = new St.BoxLayout();
    this.label = new St.Label({ text: "---Hwch25 Label---", y_align: Clutter.ActorAlign.CENTER, style: 'margin-right: 12px;' })
    this.box.add_child(this.label);
    this.add_child(this.box);
    this.menu_item = [];
    this.sub_menu_item = new Map();



    this._init_values();

    this._render_menu();
    this._update_metrics();


  }

  _init_values() {
    this.feature = ['CPU温度', 'SSD温度', 'WIFI温度', 'GPU温度', 'CPU使用', 'CPU频率'];
    this.feature_activations = [1, 1, 1, 1, 1];
    this.feature_function = this._get_temerature;
    this.info_all = ['CPU温度\t0℃', 'SSD温度\t0℃', 'WIFI温度\t0℃', 'GPU温度\t0℃', 'CPU使用\t0%', 'CPU频率\t0.00GHz'];

    
    // 从ettings中获取值
    this.refresh_time = this._settings.get_string("delay-second");

  }

  _update_menu() {

    for (let i = 0; i < this.menu_item.length; i++) {
      this.menu_item[i].label.text = `${this.info_all[i]}`;
    }
  }
  _render_menu() {
    // 删除以前的菜单项
    this.menu.removeAll();

    for (let i = 0; i < this.feature.length; i++) {
      const item = new PopupMenuItem(this.info_all[i], { can_focus: true, hover: true, reactive: true });
      this.menu.addMenuItem(item);
      this.menu_item.push(item);
    }
    // Create submenu for customization and create menu item
    const customizationSubMenu = new PopupSubMenuMenuItem("延时(s)");

    function save_settings() {
      this._settings.set_string('delay-second', this.refresh_time);
    }

    // 更改刷新时间为1S
    let lbl = '';
    if (this.refresh_time === '1') {
      lbl = '1S√';
    } else {
      lbl = '1S';
    }
    const change_delimiter = new PopupMenuItem(lbl, { can_focus: true, hover: true, reactive: true });
    this.sub_menu_item.set('1S', change_delimiter);
    change_delimiter.connect('activate', () => {
      this.refresh_time = '1';

      save_settings.call(this);
      for (const [key, value] of this.sub_menu_item) {
        if (key.substring(0, 2) === '1S') {
          value.label.text = '1S√';
        } else {
          value.label.text = key.substring(0,2);
        }
      }
    });
    customizationSubMenu.menu.addMenuItem(change_delimiter);

    // 更改刷新时间为2S
    if (this.refresh_time === '2') {
      lbl = '2S√';
    } else {
      lbl = '2S';
    }
    const add_padding = new PopupMenuItem(lbl, { can_focus: true, hover: true, reactive: true });
    this.sub_menu_item.set('2S√', add_padding);
    add_padding.connect('activate', () => {
      this.refresh_time = '2';
      save_settings.call(this);
      for (const [key, value] of this.sub_menu_item) {
        if (key.substring(0, 2) === '2S') {
          value.label.text = '2S√';
        } else {
          value.label.text = key.substring(0, 2);
        }
      }
    });
    customizationSubMenu.menu.addMenuItem(add_padding);

    // 更改刷新时间为5S
    if (this.refresh_time === '5') {
      lbl = '5S√';
    } else {
      lbl = '5S';
    }
    const rem_padding = new PopupMenuItem(lbl, { can_focus: true, hover: true, reactive: true });
    this.sub_menu_item.set('5S', rem_padding);
    rem_padding.connect('activate', () => {
      this.refresh_time = '5';
      save_settings.call(this);
      for (const [key, value] of this.sub_menu_item) {
        if (key.substring(0, 2) === '5S') {
          value.label.text = '5S√';
        } else {
          value.label.text = key.substring(0, 2);
        }
      }
    });
    customizationSubMenu.menu.addMenuItem(rem_padding);

    // 将自定义子菜单添加到主菜单
    this.menu.addMenuItem(customizationSubMenu);
  }

  // 更新温度 (如果未激活，则不调用函数)
  _update_metrics() {
    this.feature_function().then(info => {
      let value = "";
      for (let i = 0; i < this.feature.length; i++) {
        this.info_all[i] = `${this.feature[i]}\t${info[i]}`;
      }

      for (let i = 0; i < this.feature_activations.length; i++) {
        if (this.feature_activations[i]) {
          this.label.set_text(`${this.feature[i]}  ${info[i]}`);
          break;
        }
      }

    }).catch((err) => { console.error(err) });

    this._update_menu();

    // Remove existing timeout if any
    if (this._timeout) { GLib.source_remove(this._timeout); }

    // Set a timeout to refresh metrics
    const priority = GLib.PRIORITY_DEFAULT_IDLE;
    this._timeout = GLib.timeout_add_seconds(priority, parseInt(this.refresh_time), () => {
      this._update_metrics();
      return true;
    });
  }
  async _get_temerature() {

    let ret = ['0℃', '0℃', '0℃', '0℃', '0%', '0.00GHz'];

    const proc = Gio.Subprocess.new(['sensors'],
      Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);

    const [stdout, stderr] = await proc.communicate_utf8_async(null, null);

    
    if (proc.get_successful()) {
      const lines = stdout.split('\n');
      for (let i = 0; i < lines.length; i++) {
        if (lines[i].startsWith('Package id')) {//CPU
          const temps = lines[i].match(/\d+\.\d+°C/g);
          if (temps != null && temps.length > 0) {
            ret[0] = temps[0];//第一个值
          }

        } else if (lines[i].startsWith('Sensor ')) { //SSD
          const temps = lines[i].match(/\d+\.\d+°C/g);
          if (temps != null && temps.length > 0) {
            ret[1] = temps[0];//第一个值
          }

        } else if (lines[i].startsWith('temp1:')) { //WIFI
          const temps = lines[i].match(/\d+\.\d+°C/g);
          if (temps != null && temps.length > 0) {
            ret[2] = temps[0];//第一个值
          }

        }
      }
      
      console.log('*****************CPU');
    } else {
      console.log('+++++++++++++++++CPU');
      logError(stderr);
    }

    const nvidia = Gio.Subprocess.new(['nvidia-smi', '--query-gpu=temperature.gpu', '--format=csv,noheader'],
      Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);

    const [stdout1, stderr1] = await nvidia.communicate_utf8_async(null, null);
    if (nvidia.get_successful()) {
      ret[3] = stdout1.trim() + '°C';
      console.log('*****************GPU');
    } else {
      console.log('+++++++++++++++++GPU');
      logError(stderr);
    }

    let _info = this._get_cpu_info();
    ret[4] = `${_info[0]}%`;
    ret[5] = `${_info[1]}GHz`;

    return ret;

  }
  

  _file_open(file_path) {
    try {
      // Get file name and return lines of the file as array
      const file = Gio.File.new_for_path(file_path);
      const [, content] = file.load_contents(null);
      const text_decoder = new TextDecoder("utf-8");
      const content_str = text_decoder.decode(content);
      return content_str.split('\n');
    }
    catch (e) { console.error(`PROCESSING ERROR IN FILE: ${file_path} \n ${e}`); }
  }

  _get_cpu_info() {

    let cpu_usage = 0;
    let ghz_value = 0.0;
    let content_lines = undefined;
    // CPU使用率
    try {
      content_lines = this._file_open('/proc/stat');

      let current_cpu_used = 0;
      let current_cpu_total = 0;
      let current_cpu_usage = 0;

      for (const content_line of content_lines) {
        const fields = content_line.trim().split(/\s+/);

        if (fields[0] === 'cpu') {
          const nums = fields.slice(1).map(Number);
          const idle = nums[3];
          const iowait = nums[4] || 0; // 包括 iowait，如果不存在，则默认为 0

          current_cpu_total = nums.slice(0, 4).reduce((a, b) => a + b, 0) + iowait;
          current_cpu_used = current_cpu_total - idle - iowait;

          // 确保在第一次运行时设置以前的值
          this.prev_used = this.prev_used || current_cpu_used;
          this.prev_total = this.prev_total || current_cpu_total;

          // 将 CPU 使用率计算为与上次测量值的差值
          const total_diff = current_cpu_total - this.prev_total;
          const used_diff = current_cpu_used - this.prev_used;

          if (total_diff > 0) { // 避免除零
            current_cpu_usage = (used_diff / total_diff) * 100;
            cpu_usage = current_cpu_usage.toFixed(0).toString();
          }

          // 存储当前值以供下次计算
          this.prev_used = current_cpu_used;
          this.prev_total = current_cpu_total;

          break; // 在处理第一行 'cpu' 后结束
        }
      }
    } catch (e) { console.error(`CPU USAGE UPDATE FAILED: `, e); }

    // CPU GHz -----------------------------------------------------------------------
    try {
      content_lines = undefined;
      content_lines = this._file_open('/proc/cpuinfo');

      let mhz_count = 0;
      let cpu_count = 0;

      for (const content_line of content_lines) {
        const fields = content_line.trim().split(/\s+/);

        // 检查元素 1 和 2 是否为字段中的 'cpu' 和 'MHz' = “cpu MHz ： 3000.000”
        if (fields[0] === 'cpu' && fields[1] === 'MHz') {
          mhz_count += parseInt(fields[3]);
          cpu_count++;
        }
      }
      // 转换为GHz
      ghz_value = (mhz_count / cpu_count) / 1000;

    } catch (e) { console.error(`CPU GHZ UPDATE FAILED: `, e); }

    return [cpu_usage, ghz_value.toFixed(2)];
  }
  

  // Stop updates
  stop() {
    if (this._timeout) {
      GLib.source_remove(this._timeout);
    }
    this._timeout = undefined;
  }
}

// Register the HwchTemp class
GObject.registerClass({ GTypeName: 'HwchTemp' }, HwchTemp);

// Export the main extension class
export default class SystemMonitorExtension extends Extension {
  _indicator;

  // Enable the extension
  enable() {
    const settings = this.getSettings('org.gnome.shell.extensions.hwchtemp');
    this._indicator = new HwchTemp(settings);
    panel.addToStatusArea('HwchTemp', this._indicator);
  }

  // Disable the extension
  disable() {
    this._indicator.stop();
    this._indicator.destroy();
    this._indicator = undefined;
  }
}
